/*
 * Copyright 2009 Cedric Priscal
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package android_serialport_api.sample;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;

import org.kaer.election.R;
import org.kaer.election.utils.DataTransfer;
import org.kaer.election.utils.LogUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;

import android_serialport_api.SerialPort;
import android_serialport_api.SerialPortFinder;
import android_serialport_api.SerialPortInterface;

public class SerialPortReader {

    protected SerialPort mSerialPort;
    protected OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;
    private DataTransfer mTransfer;
    private int transLen;// 缓冲区中的长度
    private int dataLen;// 等待的数据长度
    private byte[] head;
    private boolean readFlag;
    public SerialPortFinder mSerialPortFinder = new SerialPortFinder();
    private static SerialPortReader mSerialPortReader;
    private SerialPortInterface callback;

    public static SerialPortReader getInstance() {
        if (mSerialPortReader == null)
            mSerialPortReader = new SerialPortReader();
        return mSerialPortReader;
    }

    private SerialPortReader() {

    }

    public void setSerialPortInterface(SerialPortInterface callback) {
        this.callback = callback;
    }

    private SerialPort getSerialPort() throws SecurityException, IOException, InvalidParameterException {
        if (mSerialPort == null) {
            /* Read serial port parameters */
//            String path = "dev/ttyS2";
            String path = "dev/ttyS3";
            int baudrate = 115200;

			/* Check parameters */
            if ((path.length() == 0) || (baudrate == -1)) {
                throw new InvalidParameterException();
            }

			/* Open the serial port */
            LogUtils.e("path= " + path);
            mSerialPort = new SerialPort(new File(path), baudrate, 0);
        }
        return mSerialPort;
    }

    private void closeSerialPort() {
        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
        }
    }

    private class ReadThread extends Thread {

        @Override
        public void run() {
            super.run();

            while (readFlag) {
                int size;
                try {
                    byte[] buffer = new byte[64];
                    if (mInputStream == null)
                        return;
                    size = mInputStream.read(buffer);
                    if (size > 0) {
                        mTransfer.AddData(buffer, size);
                        getDataFromBuffer();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                    return;
                }
            }
        }
    }

    private void initBuffer() {
        dataLen = -1;
        mTransfer = new DataTransfer(2048);
    }

    public void write(byte[] data) throws IOException {
        if (mOutputStream != null) {
            mOutputStream.write(data);

        }
    }

    private synchronized void getDataFromBuffer() {
        // 处理接收到得数据
        if (dataLen == -1) {
            if (waitHeadData()) {
                waitBottomData();
            }
        } else {
            waitBottomData();
        }

    }

    private synchronized boolean waitHeadData() {
        transLen = mTransfer.GetDataLen();

        if (transLen >= 7) {
            head = new byte[7];
            mTransfer.ReadMultiData(head, head.length);
            dataLen = head[5] * 256 + head[6];
            // LogUtils.i("head array " + ByteUtils.formatData(head));
            return true;
        }
        return false;
    }

    private synchronized void waitBottomData() {
        transLen = mTransfer.GetDataLen();
        // LogUtils.i("dataLen=" + dataLen);
        if (dataLen > 0 && transLen >= dataLen) {
            byte[] data = new byte[7 + dataLen];
            byte[] content = new byte[dataLen];
            mTransfer.ReadMultiData(content, content.length);
            System.arraycopy(head, 0, data, 0, head.length);
            System.arraycopy(content, 0, data, head.length, content.length);
            System.out.println("waitBottomDataEnd");
            if (callback != null)
                callback.onDataReceived(content, content.length);
            initBuffer();
        }
    }

    private void DisplayError(int resourceId, Context mContext) {
        AlertDialog.Builder b = new AlertDialog.Builder(mContext);
        b.setTitle("Error");
        b.setMessage(resourceId);
        b.setPositiveButton("OK", new OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        b.show();

    }


    public void openSerialPort(Context mContext) {
        try {
            mSerialPort = getSerialPort();
            mOutputStream = mSerialPort.getOutputStream();
            mInputStream = mSerialPort.getInputStream();

            initBuffer();
            /* Create a receiving thread */
            readFlag = true;
            mReadThread = new ReadThread();
            mReadThread.start();
        } catch (SecurityException e) {
            e.printStackTrace();
            DisplayError(R.string.error_security, mContext);
        } catch (IOException e) {
            DisplayError(R.string.error_unknown, mContext);
        } catch (InvalidParameterException e) {
            DisplayError(R.string.error_configuration, mContext);
        }
    }

    public void closeSerailPort() {
        readFlag = false;

        try {
            if (mInputStream != null)
                mInputStream.close();
            if (mOutputStream != null)
                mOutputStream.close();

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        closeSerialPort();
        mSerialPort = null;

    }


}
